﻿using DllCallAnalysis45.Helpers;
using NuGet.Common;
using NuGet.Packaging;
using NuGet.Repositories;
using NuGet.Versioning;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace DllDependenciesAnalysis.Helpers
{
    public enum MatchingStrategy
    {
        Complete,
        Closest,
        Latest,
    }

    public class DownloadPakcageResult
    {
        public bool Success { get; set; }

        public string Url { get; set; }

        public Version MatchVersion { get; set; }
    }

    public class NugetHelper
    {
        public static string[] NugetUrl = new string[]
        {
            "http://10.129.8.151/nuget",
            "https://api.nuget.org/v3/index.json",
        };

        public const string NugetDir = "packages";

        public static DownloadPakcageResult TryDownloadPackage(NugetRuntime runtime, string name, Version matchVersion, MatchingStrategy matching, CancellationToken cancellationToken)
        {
            var resource = runtime.Resource;
            var cache = runtime.Cache;
            var url = runtime.NugetUrl;

            ILogger logger = new ConsleLogger();

            NuGetVersion packageVersion = null;

            Console.WriteLine($"尝试处理 {name} {matchVersion}");

            var allVersions = resource.GetAllVersionsAsync(name, cache, logger, cancellationToken)
                .Result
                ?.Where(n => n.IsPrerelease != true)
                .ToList();

            if (allVersions?.Any() != true)
            {
                Console.WriteLine($"{name} 无法找到有效的版本信息");
                return new DownloadPakcageResult { };
            }

            var completeVersion = new NuGetVersion(matchVersion);

            if (matching == MatchingStrategy.Complete)
            {
                if (allVersions.Contains(completeVersion) != true)
                    return new DownloadPakcageResult();

                packageVersion = completeVersion;
            }
            else
            {
                if (allVersions.Contains(completeVersion))
                {
                    packageVersion = completeVersion;

                    // 成功查找到匹配的版本号
                    Console.WriteLine($"成功找到匹配的版本号 {packageVersion}");
                }
                else
                {
                    Console.WriteLine($"版本信息列表如下");
                    foreach (var v in allVersions)
                    {
                        Console.WriteLine(v);
                    }

                    // 找到最匹配的版本
                    if (matching == MatchingStrategy.Latest)
                    {
                        packageVersion = allVersions.OrderByDescending(n => n.Version.GetLongHashCode()).First();

                        Console.WriteLine($"{name} 依赖无版本信息，直接尝试寻找最新的版本 {packageVersion}");
                    }
                    else
                    {
                        var matchLongVersion = matchVersion.GetLongHashCode();
                        packageVersion = allVersions
                            .Where(n => n.Version.GetLongHashCode() >= matchLongVersion)
                            .OrderBy(n => n.Version.GetLongHashCode())
                            .FirstOrDefault();

                        Console.WriteLine($"{name} 无法找到完全匹配的版本 {matchVersion} 尝试匹配最接近的版本 {packageVersion}");
                    }
                }
            }


            if (packageVersion == null)
            {
                Console.WriteLine($"无法在服务器（{url}）找到完全匹配的数据");
                return new DownloadPakcageResult
                {
                };
            }

            var PathResolver = new VersionFolderPathResolver(NugetDir);

            var dllPath = PathResolver.GetInstallPath(name, packageVersion); //Path.Combine(NugetDir, $"{name}/{packageVersion.Version}");
            if (Directory.Exists(dllPath))
            {
                return new DownloadPakcageResult
                {
                };
            }

            Directory.CreateDirectory(dllPath);

            var nugetFile = PathResolver.GetPackageFilePath(name, packageVersion);

            using (FileStream fs = new FileStream(nugetFile, FileMode.OpenOrCreate))
            {
                resource.CopyNupkgToStreamAsync(name, packageVersion, fs, cache, logger, cancellationToken).Wait();
            }

            // 同时创建检查文件
            FileCheckHelper.CreateFileSha512CheckFile(nugetFile, PathResolver.GetHashPath(name, packageVersion));

            // 将对应的内部文件还原出来
            ZipHelper.UnzipFile(nugetFile, dllPath);

            return new DownloadPakcageResult
            {
                Success = true,
                Url = runtime.NugetUrl,
                MatchVersion = packageVersion.Version,
            };
        }

        public static Dictionary<string, Version> GetShouldReductionVersion(List<AssemblyName> missNames)
        {
            if (missNames?.Any() != true)
                return null;

            var mapping = new Dictionary<string, Version>();

            foreach (var item in missNames)
            {
                var name = item.Name;

                if (mapping.TryGetValue(name, out var version) != true)
                {
                    mapping[name]
                        = version
                        = Version.Parse("1.0.0.0");
                }

                if (item.Version.GetLongHashCode() > version.GetLongHashCode())
                {
                    mapping[name] = item.Version;
                }
            }

            return mapping;
        }

        public static bool CopyNugetPackageFiles(string packageDir, string packageId, Version version, string target, string platform = "net45")
        {
            var pathResolver = new VersionFolderPathResolver(packageDir);
            //var nuspecFile = pathResolver.GetManifestFileName(packageId, new NuGetVersion(version));
            //var nuspecReader = new NuspecReader(nuspecFile);

            var packagePath = pathResolver.GetInstallPath(packageId, new NuGetVersion(version));
            var dllPath = Path.Combine(packagePath, "lib", platform);

            if (Directory.Exists(dllPath) != true)
                return false;

            var files = Directory.GetFiles(dllPath);

            foreach (var file in files)
            {
                var targetFile = Path.Combine(target, Path.GetFileName(file));

                for (int i = 0; i < 5; i++)
                {
                    try
                    {
                        File.Copy(file, targetFile, true);
                        break;
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine($"尝试复制文件{file}=>{targetFile}时出错 {ex}");
                    }
                }
            }

            return true;
        }

        [Obsolete("这个函数管的太宽了，拆一下", true)]
        public static Dictionary<string, Version> Reduction(List<AssemblyName> allErroNames)
        {
            var errorMappings = allErroNames
                   .GroupBy(n => n.Name)
                   .ToDictionary(n => n.Key, n => n.Select(m => m.Version).OrderByDescending(m => m.GetLongHashCode()).ToList());

            Console.WriteLine($"总共出现了 {errorMappings.Count} 条缺失的内容");

            var mappings = new Dictionary<string, Version>();

            var runtimes = NugetUrl.Select(n => new NugetRuntime(n)).ToList();

            CancellationToken cancellationToken = CancellationToken.None;

            foreach (var item in errorMappings)
            {
                var name = item.Key;
                var versions = item.Value;

                if (versions.Count > 1)
                {
                    Console.WriteLine($"{name} 出现了多条不同版本的依赖信息 {string.Join("、", versions)}");
                }

                var matchVersion = versions.First();

                var find = false;

                foreach (var runtime in runtimes)
                {
                    var version = NugetHelper.TryDownloadPackage(runtime, name, matchVersion, MatchingStrategy.Closest, cancellationToken);
                    if (version != null)
                    {
                        find = true;
                        mappings[name] = version.MatchVersion;

                        break;
                    }
                }

                if (find != true)
                    Console.WriteLine($"{name} {matchVersion} 还原版本失败 ");
            }

            return mappings;
        }
    }
}
